# -*- coding: UTF-8 -*-
"""
@Project : FastTortoiseDemo
@File    : .py
@IDE     : PyCharm
@Author  : xuan
@Date    : 2024/3/7 16:28
"""
from passlib.context import CryptContext
from Apps.user.user_schemas import UserInDB, TokenData, Token

from Apps.user.user_models import User
from jose import JWTError, jwt
from fastapi.security import OAuth2PasswordBearer
from typing import Union
from datetime import datetime, timedelta, timezone
from fastapi import Depends, HTTPException, status

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/login")
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"  # 加密算法
ACCESS_TOKEN_EXPIRE_MINUTES = 15  # 过期时间


def get_password_hash(password):
    """
    密码加密
    :param password:
    :return:
    """
    return pwd_context.hash(password)


def verify_password(plain_password, hashed_password):
    """
    密码解密
    :param plain_password:
    :param hashed_password:
    :return:
    """
    return pwd_context.verify(plain_password, hashed_password)


async def get_user(username: str) -> UserInDB:
    """
    获取用户
    :param username:
    :return:
    """
    user = await User.filter(username=username, is_deleted=0).first()
    if not user:
        return None

    return UserInDB(
        id=str(user.id),
        username=user.username,
        hashed_password=user.password,
        is_deleted=user.is_deleted
    )


async def authenticate_user(username: str, password: str):
    """
    验证用户 是否有效
    :param username:
    :param password:
    :return:
    """
    user = await get_user(username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user


def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None):
    """
    创建 Token
    :param data:
    :param expires_delta:
    :return:
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


async def get_current_user(token: str = Depends(oauth2_scheme)):
    """
    获取当前用户
    :param token:
    :return:
    """
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无法验证凭据",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except JWTError:
        raise credentials_exception
    user = await get_user(username=token_data.username)
    if user is None:
        raise credentials_exception
    return user


async def get_current_active_user(current_user: User = Depends(get_current_user)):
    """
    获取当前活跃用户
    :param current_user:
    :return:
    """
    if current_user.is_deleted:
        raise HTTPException(status_code=400, detail="不可用的账号")
    return current_user


async def login_for_access_token(current_user):
    user = await authenticate_user(current_user.username, current_user.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户名或密码错误",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    return Token(access_token=access_token, token_type="bearer")
